Udacity RoboND - Rover project 簡介


Posted by Po-Jen on 2017-08-26

前言

最近 Udacity 的 nano degree 算是滿紅的,他們也有把很多教學資源放到 github 上面,例如 Rover project,我覺得拿來上手還不錯,以下就稍微來簡介一下這個 project 有趣的地方。

Project 簡介

Rover圖

這個 project 是要讓 Rover 在世界中自由自在地行走,然後尋找特定的岩石樣本並收集,因為他們將岩石的顏色設計得跟背景很不一樣,所以我們只要可以在環境中走來走去,然後一直尋找畫面中有沒有黃黃的、像是黃金的石頭就好了:

sample

對收到的影像做處理,得到可以走的區塊

首先,機器人要知道哪邊可以走、哪邊不能走,所以在收到 camera 的影像後,要先處理一下,濾出可以走的區塊,

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

image_name = 'sample.jpg'
image = mpimg.imread(image_name)

# 因為可以走的區塊是白色的,使出color threshold大法
def color_thresh(img, rgb_thresh=(0, 0, 0)):
 color_select = np.zeros_like(img[:,:,0])
 above_thresh = (img[:,:,0] > rgb_thresh[0]) \
       & (img[:,:,1] > rgb_thresh[1]) \
 & (img[:,:,2] > rgb_thresh[2])
 color_select[above_thresh] = 1

 return color_select
 red_threshold = 150
 green_threshold = 150
 blue_threshold = 150

 rgb_threshold = (red_threshold, green_threshold, blue_threshold)

# pixels below the thresholds
colorsel = color_thresh(image, rgb_thresh=rgb_threshold)

# Display the original image and binary
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(21, 7), sharey=True)
f.tight_layout()
ax1.imshow(image)
ax1.set_title('Original Image', fontsize=40)

ax2.imshow(colorsel, cmap='gray')
ax2.set_title('Your Result', fontsize=40)
plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
plt.show()

這樣就可以畫出結果了:

color

產生地圖

雖然我們從影像中,得知了目前可以走的區塊,但隨著機器人一直走來走去,就會得到一張張的影像,每張裡面都有可以走的區塊。如果可以把這一張張的影像接起來,變成地圖,那事情就會方便許多!所以我們可以透過 perspective transform 將影像變成從俯視的 view 觀看,然後再將影像轉換到以 Rover 為中心的坐標系就可以了。

import numpy as np
import cv2
import matplotlib.image as mpimg

def perspect_transform(img, src, dst):

# Get transform matrix using cv2.getPerspectivTransform()
 M = cv2.getPerspectiveTransform(src, dst)
# Warp image using cv2.warpPerspective()
# keep same size as input image
warped = cv2.warpPerspective(img, M, (img.shape[1], img.shape[0]))
# Return the result
return warped
def color_thresh(img, rgb_thresh=(160, 160, 160)):
# Create an array of zeros same xy size as img, but single channel
 color_select = np.zeros_like(img[:,:,0])
# Require that each pixel be above all thre threshold values in RGB
# above_thresh will now contain a boolean array with "True"
# where threshold was met
above_thresh = (img[:,:,0] > rgb_thresh[0]) \
      & (img[:,:,1] > rgb_thresh[1]) \
      & (img[:,:,2] > rgb_thresh[2])
# Index the array of zeros with the boolean array and set to 1
      color_select[above_thresh] = 1
# Return the binary image
      return color_select
      image = mpimg.imread('sample.jpg')

      dst_size = 5
      bottom_offset = 6

# 這邊的定義是要給 perspective transform 用的,先定義原圖中的某些點,再定義想要扭成某個形狀的圖中那些點的位置
      source = np.float32([[14, 140], [301 ,140],[200, 96], [118, 96]])
destination = np.float32([[image.shape[1]/2 - dst_size, image.shape[0] - bottom_offset],
  [image.shape[1]/2 + dst_size, image.shape[0] - bottom_offset],
  [image.shape[1]/2 + dst_size, image.shape[0] - 2*dst_size - bottom_offset],
  [image.shape[1]/2 - dst_size, image.shape[0] - 2*dst_size - bottom_offset],
  ])
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import cv2
from extra_functions import perspect_transform, color_thresh, source, destination

image = mpimg.imread('sample.jpg')

# 轉換到以 Rover 為中心的座標系
def rover_coords(binary_img):
 y_pos, x_pos = binary_img.nonzero()
 x_pixel = -(y_pos - binary_img.shape[0])
 y_pixel = -(x_pos - binary_img.shape[1]/2 )
 return x_pixel, y_pixel

# Perform warping and color thresholding
 warped = perspect_transform(image, source, destination)
colorsel = color_thresh(warped, rgb_thresh=(160, 160, 160))
xpix, ypix = rover_coords(colorsel)

# Plot the map in rover-centric coords
fig = plt.figure(figsize=(5, 7.5))
plt.plot(xpix, ypix, '.')
plt.ylim(-160, 160)
plt.xlim(0, 160)
plt.title('Rover-Centric Map', fontsize=20)

綜合起來,就可以得到

warped_map

根據地圖產生新的移動方向

假設已經有了地圖,下一步就是希望得到要走的方向,最簡單的想法就是挑可以走的範圍的中線,因為離障礙物最遠。但我們前面的表示法,是把每個 pixel 用 (x,y) 這種笛卡爾坐標系下的定義來儲存位置,即便我們有這些可行走區域的 (x,y) 位置,要從這些 (x,y) pair 中找出中線還是頗麻煩的。

所以這邊就會用到小時候學過的極座標啦,大家可以想一想,如果我們有每一個可走區域的 pixel 的極座標,那是不是把每一個點的角度都平均一下,就可以得到要行走的角度了呢?

根據上面的觀念,我們就再寫一下從笛卡爾座標轉換成極座標的 function:

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
from extra_functions import perspect_transform, color_thresh, rover_coords

# 只要定義一個將pixel從笛卡爾座標轉成極座標的function就好
def to_polar_coords(xpix, ypix):
 dist = np.sqrt(xpix**2 + ypix**2)
 angles = np.arctan2(ypix, xpix)
 return dist, angles

 image = mpimg.imread('angle_example.jpg')
 warped = perspect_transform(image) # Perform perspective transform
 colorsel = color_thresh(warped, rgb_thresh=(160, 160, 160)) # Threshold the warped image
 xpix, ypix = rover_coords(colorsel) # Convert to rover-centric coords
 distances, angles = to_polar_coords(xpix, ypix) # Convert to polar coords
 avg_angle = np.mean(angles) # Compute the average angle

# Plot
 fig = plt.figure(figsize=(12,9))
plt.subplot(221)
plt.imshow(image)
plt.subplot(222)
plt.imshow(warped)
plt.subplot(223)
plt.imshow(colorsel, cmap='gray')
plt.subplot(224)
plt.plot(xpix, ypix, '.')
plt.ylim(-160, 160)
plt.xlim(0, 160)
arrow_length = 100
x_arrow = arrow_length * np.cos(avg_angle)
y_arrow = arrow_length * np.sin(avg_angle)
plt.arrow(0, 0, x_arrow, y_arrow, color='red', zorder=2, head_width=10, width=2)
plt.show()

就可以畫出下圖:

decision

總結

今天跟大家簡單介紹了一下 Udacity 的學習資源,也簡介了一點點 Rover project 的內容,我自己覺得他們的 project 設計都還不錯,值得玩一玩,適合想要入門的讀者參考。

延伸閱讀

  1. RoboND Kinematics Project
  2. RoboND Perception Project

關於作者:
@pojenlai 演算法工程師,對機器人跟電腦視覺有少許研究,最近在學習看清事物的本質與改進自己的觀念


#Robot #navigation #rover









Related Posts

最小生成樹(Minimum Spanning Tree, MST)

最小生成樹(Minimum Spanning Tree, MST)

該如何入門 CTF 中的 Web 題?

該如何入門 CTF 中的 Web 題?

什麼是 Pure Function?在 React 當中的重要性是什麼?

什麼是 Pure Function?在 React 當中的重要性是什麼?




Newsletter




Comments